package panels;

import game.Game;
import game.GameDriver;
import game.Round;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.Serializable;
import java.util.Random;

import javax.swing.JOptionPane;
import javax.swing.JPanel;

import tiles.Mountain;
import tiles.Plain;
import tiles.River;
import tiles.Tile;
import tiles.Town;

/**
 * Panel where the map is displayed
 * 
 * @author Kevin Shin
 */
public class MapPanel extends JPanel implements Serializable {

    /** Auto-generated serial version ID. */
    private static final long serialVersionUID = 6648103645286408293L;
    private transient Game currentGame;
    private transient GameDriver driver;
    private transient Round round;

    private static final int NUM_ROWS = 5; // number of rows
    private static final int NUM_COLS = 9; // number of columns

    private Tile[][] terrainGrid = new Tile[NUM_ROWS][NUM_COLS]; // 2D map array
    // of tiles

    JPanel map;// the actual map panel
    JPanel infoPane; // where the timer and other info is stored
    JPanel timer; // contains JLabel with the timer number
    JPanel total;// holds both the map and the infoPane

    public MapPanel(Game currentGame, GameDriver driver, Round round) {
	this.currentGame = currentGame;
	this.driver = driver;
	this.round = round;

	if (currentGame.getMapType() == 1) { // standard mapType
	    Tile[][] standard = {
		    { new Plain(), new Plain(), new Mountain(1), new Plain(),
			    new River(), new Plain(), new Mountain(3),
			    new Plain(), new Plain() },
		    { new Plain(), new Mountain(1), new Plain(), new Plain(),
			    new River(), new Plain(), new Plain(), new Plain(),
			    new Mountain(3) },
		    { new Mountain(3), new Plain(), new Plain(), new Plain(),
			    new Town(), new Plain(), new Plain(), new Plain(),
			    new Mountain(1) },
		    { new Plain(), new Mountain(2), new Plain(), new Plain(),
			    new River(), new Plain(), new Mountain(2),
			    new Plain(), new Plain() },
		    { new Plain(), new Plain(), new Mountain(2), new Plain(),
			    new River(), new Plain(), new Plain(), new Plain(),
			    new Mountain(2) } };
	    terrainGrid = standard;

	} else if (currentGame.getMapType() == 2) { // random mapType
	    Random r = new Random(); // random generator
	    for (int i = 0; i < NUM_ROWS; i++) {
		for (int j = 0; j < NUM_COLS; j++) {
		    if (j == NUM_COLS / 2) { // if it's the middle column
			if (i == NUM_ROWS / 2) // and the middle row
			    terrainGrid[i][j] = new Town(); // create town tile
			else
			    terrainGrid[i][j] = new River(); // create river
			// tile
		    } else
			// every other column
			terrainGrid[i][j] = new Plain(); // create plain tile
		}
	    }

	    // add 2 mountains randomly to each row

	    for (Tile[] arr : terrainGrid) { // for each row in terrainGrid
		int randIdx1 = r.nextInt(4); // random index between [0,4)
		int randIdx2 = r.nextInt(4) + 5; // random index between [5,9)
		int randType1 = r.nextInt(3) + 1; // random int between [0,3)
		int randType2 = r.nextInt(3) + 1; // random int between [0,3)
		arr[randIdx1] = new Mountain(randType1); // create mountain at
		// index
		arr[randIdx2] = new Mountain(randType2); // create mountain at
		// index
	    }
	    
	}

	map = new JPanel();
	map.setLayout(new GridLayout(5, 9));

	// adds tiles to grid
	for (int i = 0; i < NUM_ROWS; i++) {
	    for (int j = 0; j < NUM_COLS; j++) {
		map.add(terrainGrid[i][j]);
	    }
	    add(map);
	}
	add(map);

	setPreferredSize(currentGame.fullPreferredSize());

	for (int i = 0; i < NUM_ROWS; i++) {
	    for (int j = 0; j < NUM_COLS; j++) {
		terrainGrid[i][j].addActionListener(new ButtonListener(i, j));
	    }
	}
    }

    public Tile[][] getMap() {
	return terrainGrid;
    }

    /**
     * Reinitializes map panel.
     * 
     * @param currentGame
     * @param driver
     * @param round
     */

    public void reload(Game currentGame, GameDriver driver, Round round) {
	this.currentGame = currentGame;
	this.driver = driver;
	this.round = round;
    }

    /**
     * Button listener for when the land is being selected
     */
    class ButtonListener implements ActionListener, Serializable {

	/** Auto-generated serial version ID. */
	private static final long serialVersionUID = 6954924094682342609L;
	int x;
	int y;

	public ButtonListener(int i, int j) {
	    this.x = i;
	    this.y = j;
	}

	public void actionPerformed(ActionEvent e) {
	    Tile currentTile = terrainGrid[x][y];
	    if (currentGame.getPhase() == 0) {

		if (currentTile.isTown()) {
		    Component frame = null;
		    JOptionPane.showMessageDialog(frame,
			    "You cannot own the Town");
		} else {
		    if (currentTile.getOwner() != null) {
			Component frame = null;
			JOptionPane
				.showMessageDialog(frame,
					"A Player already owns this tile. Please choose another.");
		    } else {
			currentTile.playerBorder(round.getCurrPlayer()
				.getColor());
			round.getCurrPlayer().addProperty(currentTile);
			currentTile.setOwner(round.getCurrPlayer());
			currentTile.setUpgradeDegree(currentGame.getDifficulty().getUpgradeDegree());

			if (round.getRoundZeroCount() < currentGame
				.playerArraySize() * 2 - 1) {
			    round.setRoundZeroCount(round.getRoundZeroCount() + 1);
			    round.incrementPlayerInt();
			    round.landSelection();
			} else {
			    round.setPlayerInt(0);
			    round.takeTurn();
			}
		    }
		}
	    }

	    else if (currentTile.hasWompus()) {//if there is a wompus on the tile
		JOptionPane.showMessageDialog(null, "You found the wompus and earned $"+round.getWompus().getValue());
		round.getWompus().findWompus(getCursorType());
		round.getCurrPlayer().getResourceBasket().addResource(4, round.getWompus().getValue());
		round.updateInfoPanel();
	    }

	    else {//if there is no wompus on the tile

		if (currentGame.getPhase() == 1) {
		    if (currentTile.isTown()){
			currentTile.setBorder(null);
			driver.toPanel(round.getTownPanel(),
				BorderLayout.CENTER);
		    }
		    else {
			round.getCurrPlayer().placeMule(currentTile);
			round.getMapPanel()
				.setCursor(
					Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		    }
		}

		else if (currentGame.getPhase() == 4) {
		    if (currentTile.isTown())
			JOptionPane.showMessageDialog(null, "Thats the town");
		    else
			JOptionPane.showMessageDialog(
				null,
				"Crystite Production Rate: "
					+ Integer.toString(currentTile
						.getProductionRate(3))
					+ " Crystite per turn");
		    currentGame.setPhase(1);
		}

		else if (currentGame.getPhase() == 5) {
		    if (currentTile.isTown())// if attack the town
			JOptionPane.showMessageDialog(null,
				"You can't attack the town, try again");
		    else if (!currentTile.hasOwner()) {
			JOptionPane.showMessageDialog(null,
				"This tile has no owner!");
		    } else if (currentTile.getOwner().equals(
			    round.getCurrPlayer())
			    && round.getCurrPlayer().getAttackDefend() == 1)// attacking
									    // themselves
			JOptionPane.showMessageDialog(null,
				"You can't attack yourself, try again");
		    else {// modifying correctly
			currentTile.attackDefend(round.getCurrPlayer()
				.getAttackDefend());
			round.getMapPanel()
				.setCursor(
					Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
			round.getCurrPlayer().setNeutral();
			currentGame.setPhase(1);
		    }
		}

		else if (currentGame.getPhase() == 3) {

		    if (currentTile.isTown()) {
			Component frame = null;
			JOptionPane.showMessageDialog(frame,
				"You cannot own the Town");
		    } else {
			if (currentTile.getOwner() != null && !currentTile.getOwner().equals(round.getCurrPlayer())) {
			    Component frame = null;
			    JOptionPane
				    .showMessageDialog(frame,
					    "A Player already owns this tile. Please choose another.");
			}
			else if (currentTile.getOwner() != null && currentTile.getOwner().equals(round.getCurrPlayer())) {
			    currentTile.upgrade();
			    currentGame.setPhase(1);
			}
			else {

			    if (round.getCurrPlayer()
				    .buyLand(currentTile)) {

				currentTile.playerBorder(round
					.getCurrPlayer().getColor());
				round.getCurrPlayer().addProperty(
					currentTile);
				currentTile.setOwner(round
					.getCurrPlayer());
				round.updateInfoPanel();
				currentGame.setPhase(1);
			    } else {
				JOptionPane.showMessageDialog(null,
					"You need at least $300 to buy land");
			    }
			}
		    }
		}
	    }
	}
    }

    public Tile getTile(int x, int y) {
	return terrainGrid[x][y];
    }
    
    public Cursor getCursorType(){
	return this.getCursor();
    }

}
